#include "scene/wsyperspectivecamera.h"
#include "scene/wsyscene.h"
#include "geometry/wsymesh.h"
#include "pipedisplayopenglwidget.h"

#include <QEvent>
#include <QDebug>
#include <QTimer>

static GLuint VBO, VAO, EBO;

PipeDisplayOpenGLWidget::PipeDisplayOpenGLWidget(QWidget *parent): QOpenGLWidget (parent)
{
    m_pTimer = new QTimer(this);
    m_pTimer->setInterval(1);

    connect(m_pTimer, &QTimer::timeout, this, [=] {
       update();
    });

    m_pTimer->start();

    scene = new WSYScene();
    WSYScene *localScene = (WSYScene *)scene;
    localScene->testInstance1();
    mouseController = new MouseController(localScene, this);
    rightMouseController = new RightMouseController(localScene, this);

    transformMatrix = Matrix3D::newInstance();
    lightDirection = new GLfloat[3];

    inverseMatrixNew = Matrix3D::newInstance();
    inverseTransposeMatrixNew = Matrix3D::newInstance();
}

PipeDisplayOpenGLWidget::~PipeDisplayOpenGLWidget() {
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
}

void PipeDisplayOpenGLWidget::initializeGL() {
    this->initializeOpenGLFunctions();

    bool success = shaderProgram.addShaderFromSourceFile(
                QOpenGLShader::Vertex, ":/shaderTexts/vertexshaderofpipe.vsh");
    if (success) {
        qDebug() << "Succeeded in loading vertex shader";
    } else {
        qDebug() << "Add vertex shader from source file failed!" << shaderProgram.log();
        return;
    }

    success = shaderProgram.addShaderFromSourceFile(
                QOpenGLShader::Fragment, ":/shaderTexts/fragmentshaderofpipe.fsh");

    if (success) {
        qDebug() << "Succeeded in loading fragment shader";
    } else {
        qDebug() << "Add fragment shader from source file failed!";
    }

    success = shaderProgram.link();
    if (success) {
        qDebug() << "Succeeded in linking both shaders";
    } else {
        qDebug() << "Failed to link both shaders!" << shaderProgram.log();
    }


    positionAttribLocation = shaderProgram.attributeLocation("a_position");
    qDebug() << "positionAttribLocation:" << positionAttribLocation;
    normalAttribLocation = shaderProgram.attributeLocation("a_normal");
    qDebug() << "normalAttribLocation:" << normalAttribLocation;

    cameraMatrixUniformLocation = shaderProgram.uniformLocation("u_cameraMatrix");
    qDebug() << "cameraMatrixUniformLocation:" << cameraMatrixUniformLocation;

    transformMatrixUniformLocation = shaderProgram.uniformLocation("u_transformMatrix");
    qDebug() << "transformMatrixUniformLocation:" << transformMatrixUniformLocation;

    normalMatrixUniformLocation = shaderProgram.uniformLocation("u_normalMatrix");
    qDebug() << "normalMatrixUniformLocation:" << normalMatrixUniformLocation;

    modeUniformLocation = shaderProgram.uniformLocation("u_mode");
    qDebug() << "modeUniformLocation:" << modeUniformLocation;

    reverseLightDirectionUniformLocation = shaderProgram.uniformLocation("u_reverseLightDirection");
    qDebug() << "reverseLightDirectionUniformLocation:" << reverseLightDirectionUniformLocation;

    colorUniformLocation = shaderProgram.uniformLocation("u_color");
    qDebug() << "colorUniformLocation:" << colorUniformLocation;

    installed = true;
}


void PipeDisplayOpenGLWidget::resizeGL(int w, int h) {

    //获取几何体和法向量的缓冲对象
    WSYScene *localScene = (WSYScene *)scene;

    WSYPerspectiveCamera *camera = (WSYPerspectiveCamera *)localScene->getCamera();

    double newAspect = double(w) / double(h);

    //qDebug() << "Resizing" << newAspect;

    camera->setAspect(newAspect);
}

void PipeDisplayOpenGLWidget::setSingleAttribPosition(GLuint attribPosition, int size, GLfloat *positions) {
    glVertexAttribPointer(attribPosition, size, GL_FLOAT, GL_FALSE, 0, positions);
}

void PipeDisplayOpenGLWidget::paintGL() {
    if (!installed) {
        qDebug() << "Not installed!"; return;
    }
    glClearColor(0, 0.7f, 0.4f, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_CULL_FACE);

    glEnable(GL_DEPTH_TEST);

    shaderProgram.bind();

    //获取几何体和法向量的缓冲对象
    WSYScene *localScene = (WSYScene *)scene;

    WSYPerspectiveCamera *camera = (WSYPerspectiveCamera *)localScene->getCamera();

    GLfloat *cameraMatrix = camera->getCameraMatrix();

    glUniformMatrix4fv(cameraMatrixUniformLocation, 1, GL_FALSE, cameraMatrix);

    Vertex3D *cPosition = camera->getPosition(), *cLookAt = camera->getLookAt();
    //Vector3D *lightDirectionNotNormalized = Math3D::direction(cLookAt, cPosition);
    Math3D::directionNewAndNormalize(cLookAt, cPosition, lightDirection);

    //Vector3D *lightDirection = lightDirectionNotNormalized->normalize();
    glUniform3f(reverseLightDirectionUniformLocation,
                lightDirection[0],
                lightDirection[1],
                lightDirection[2]);

    //获取几何体和法向量的缓冲对象
    QList<QObject *> *geometryObjects = localScene->getGeometryObjects();
    for (int c = 0; c < geometryObjects->length(); c++) {
        //包含了移动、旋转、缩放信息的几何体对象
        WSYMesh *singleMesh = (WSYMesh *) geometryObjects->at(c);
        Geometry *geometry = singleMesh->object;
        QString type = geometry->getType();
        //if (type == "object"){
            GLfloat *vertices = geometry->verticesBuffer();
            GLfloat *normals;
            if (type == "object") normals = geometry->normalsBuffer();
            else normals = vertices;

            if (type == "line") glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
            //geometry->printVerticesWithinRange(cameraMatrix);


            glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
            glVertexAttribPointer(normalAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, normals);

            glUniform4f(colorUniformLocation, 1, 0.5f, 1, 1);
            GLfloat *transformMatrix = singleMesh->getMatrix();
            //transformMatrix = Matrix3D::newInstance();
            //Matrix3D::printSingleMatrix("transformMatrix", transformMatrix);
            glUniformMatrix4fv(transformMatrixUniformLocation, 1, GL_FALSE, transformMatrix);

            int mode = 0;
            if (type == "object") mode = 0;
            else mode = 1;
            glUniform1i(modeUniformLocation, mode);

            Matrix3D::inverseMatrixNew(transformMatrix, inverseMatrixNew);
            Matrix3D::transposeMatrixNew(inverseMatrixNew, inverseTransposeMatrixNew);

            if (type == "line") Matrix3D::attachNewInstance(inverseTransposeMatrixNew);

            glUniformMatrix4fv(normalMatrixUniformLocation, 1, GL_FALSE, inverseTransposeMatrixNew);

            glEnableVertexAttribArray(positionAttribLocation);
            glEnableVertexAttribArray(normalAttribLocation);
            if (type == "object") glDrawArrays(GL_TRIANGLES, 0, geometry->faceCount() * 3);
            else glDrawArrays(GL_LINES, 0, geometry->verticesCountOfLine());
            glDisableVertexAttribArray(normalAttribLocation);
            glDisableVertexAttribArray(positionAttribLocation);

//        } else if (type == "line") {
//            qDebug() << "The USA is at war.";
//        }
    }
    //delete lightDirection;
    //delete lightDirectionNotNormalized;
    //delete cameraMatrix;

    shaderProgram.release();
}

void PipeDisplayOpenGLWidget::mouseMoveEvent(QMouseEvent *event) {
    //qDebug() << "mouse status:"<< event->;
    if(event->buttons() & Qt::LeftButton) {
        mouseController->mouseMoveEvent(event);
    } else if(event->buttons() & Qt::RightButton) {
        rightMouseController->mouseMoveEvent(event);
    }
}

void PipeDisplayOpenGLWidget::mousePressEvent(QMouseEvent *event) {

    if(event->button() == Qt::LeftButton) {
        qDebug() << "left button pressed";
        mouseController->mousePressEvent(event);
    } else if(event->button() == Qt::RightButton) {
        qDebug() << "right button pressed";
        rightMouseController->mousePressEvent(event);
    }
}

void PipeDisplayOpenGLWidget::mouseReleaseEvent(QMouseEvent *event) {
    if(event->button() == Qt::LeftButton) {
        mouseController->mouseReleaseEvent(event);
    } else if(event->button() == Qt::RightButton) {
        rightMouseController->mouseReleaseEvent(event);
    }
}

void PipeDisplayOpenGLWidget::wheelEvent(QWheelEvent *event) {
    qDebug() << "delta: " << event->delta();
    int delta = event->delta();
    WSYScene *localScene = (WSYScene *)scene;
    WSYPerspectiveCamera *camera = (WSYPerspectiveCamera *)localScene->getCamera();
    Vertex3D *position = camera->getPosition();
    Vertex3D *lookAt = camera->getLookAt();
    Vector3D *dir = Math3D::direction(lookAt, position);
    double dirLength = dir->length();
    double zoomAmount = 1 + delta / abs(delta) * 0.1;
    Vector3D *newDir = dir->normalize()->multiply(dirLength * zoomAmount);
    Vertex3D *nCamera = lookAt->addVectors(newDir);

    camera->setPosition(nCamera->getX(), nCamera->getY(), nCamera->getZ());
}
